Skip to content

chore(playwright): Rewrite password confirmation handler to avoid race conditions#62033

Open
DerDreschner wants to merge 1 commit into
masterfrom
fix/login-on-playwright
Open

chore(playwright): Rewrite password confirmation handler to avoid race conditions#62033
DerDreschner wants to merge 1 commit into
masterfrom
fix/login-on-playwright

Conversation

@DerDreschner

@DerDreschner DerDreschner commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

The password confirmation handler on playwright is using a timeout on isVisible. That is deprecated and isn't being considered anymore, which lead to several issues during our playwright tests (especially under high load on our CI runners).

When that issue occurs, the following error can be seen in the playwright tests. The timeout was raised to 90 seconds here and the issue still happened, as the whole test just stuck because of the login never being done.

Notice:   1 failed
    [admin-settings] › tests/playwright/e2e/appstore/admin-settings-apps.spec.ts:159:2 › Settings: App management › Limit app usage to group 
  43 passed (11.2m)

  1) [admin-settings] › tests/playwright/e2e/appstore/admin-settings-apps.spec.ts:159:2 › Settings: App management › Limit app usage to group 

    Test timeout of 90000ms exceeded.

    Error: expect(locator).toHaveCount(expected) failed

    Locator:  locator('#app-sidebar-vue').getByRole('list', { name: 'Limited to groups' })
    Expected: 0
    Received: undefined


      206 |
      207 | 		// Verify the "Limited to groups" list is no longer visible
    > 208 | 		await expect(appstorePage.limitedToGroupsList()).toHaveCount(0)
          | 		                                                 ^
      209 | 	})
      210 | })
      211 |
        at /home/runner/actions-runner/_work/server/server/tests/playwright/e2e/appstore/admin-settings-apps.spec.ts:208:52

    Error Context: test-results/appstore-admin-settings-ap-4ce9a-nt-Limit-app-usage-to-group-admin-settings/error-context.md

Checklist

AI (if applicable)

  • The content of this PR was partly or fully generated using AI

Assisted-by: ClaudeCode:claude-fable-5
Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
@DerDreschner DerDreschner requested a review from a team as a code owner July 13, 2026 01:50
@DerDreschner DerDreschner requested review from nfebe, sorbaugh and susnux and removed request for a team July 13, 2026 01:50
Copilot AI requested a review from a team as a code owner July 13, 2026 02:29
Copilot AI requested review from icewind1991 and provokateurin and removed request for a team July 13, 2026 02:29
Copilot stopped work on behalf of DerDreschner due to an error July 13, 2026 02:46
Copilot stopped work on behalf of DerDreschner due to an error July 13, 2026 02:51
@DerDreschner DerDreschner reopened this Jul 13, 2026
@DerDreschner DerDreschner force-pushed the fix/login-on-playwright branch from 512a907 to 19b831c Compare July 13, 2026 02:58
@DerDreschner DerDreschner requested a review from odzhychko July 13, 2026 09:40

if (dialogVisible) {
// Fill the password field
await expect(dialog).toBeVisible({ timeout: 500 })

@odzhychko odzhychko Jul 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disclaimer: I'm not too to familiar with best-practices for Playwright.

But I'm pretty sure, expect should only be used for real assertions.

My research with Claude Opus 4.8 suggest to use waitFor. So something like this should work as expected:

const dialogVisible = await dialog
  .waitFor({ state: "visible", timeout: 500 })
  .then(() => true)
  .catch(() => false);

if (!dialogVisible) {
  return;
}

await dialog.locator('input[type="password"]').fill(password);

// Click the confirm button
await dialog.getByRole("button", { name: "Confirm" }).click();

// Wait for the dialog to disappear
await dialog.waitFor({ state: "hidden" });

Comment on lines +19 to +27
await expect(dialog).toBeVisible({ timeout: 500 })
.then(async () => {
await dialog.locator('input[type="password"]').fill(password)

// Click the confirm button
await dialog.getByRole('button', { name: 'Confirm' }).click()

// Wait for the dialog to disappear
await dialog.waitFor({ state: 'hidden' })
}
} catch {
// Dialog didn't appear, which is fine - some operations might not require confirmation
}
await expect(dialog).toBeHidden()
})
.catch(() => {
// Dialog didn't appear — some operations don't require confirmation.
})

@susnux susnux Jul 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a async function so please use proper try {} catch {} blocks :)

Suggested change
await expect(dialog).toBeVisible({ timeout: 500 })
.then(async () => {
await dialog.locator('input[type="password"]').fill(password)
// Click the confirm button
await dialog.getByRole('button', { name: 'Confirm' }).click()
// Wait for the dialog to disappear
await dialog.waitFor({ state: 'hidden' })
}
} catch {
// Dialog didn't appear, which is fine - some operations might not require confirmation
}
await expect(dialog).toBeHidden()
})
.catch(() => {
// Dialog didn't appear — some operations don't require confirmation.
})
try {
await expect(dialog).toBeVisible({ timeout: 500 })
await dialog.locator('input[type="password"]').fill(password)
await dialog.getByRole('button', { name: 'Confirm' }).click()
await expect(dialog).toBeHidden()
} catch {
// Dialog didn't appear — some operations don't require confirmation.
}

@odzhychko

Copy link
Copy Markdown
Contributor

Just a thought: handlePasswordConfirmation will always make a test wait even if no password confirmation is required. Tests could be setup with locations handlers to only run this code if the password dialog actually becomes visible.

In the future this might be useful, if a test:

  • doesn't care for when the password confirmation appears
  • but has to call handlePasswordConfirmation often because it might appear

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants